package com.svconnect.model.soap;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.reflect.Type;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.http.HttpEntity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.svconnect.exceptions.ExceptionDetail;
import com.svconnect.exceptions.FaultException;
import com.svconnect.exceptions.IException;
import com.svconnect.serialization.XMLSerializer;
import com.svconnect.util.Log;
import com.svconnect.util.StringUtil;

public class SoapResponseEnvelope {

	private Document envelope;

	public SoapResponseEnvelope(HttpEntity entity) throws IllegalStateException, IOException {
		InputStream is = entity.getContent();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        StringBuilder sb = new StringBuilder();
        for (String line = br.readLine(); line != null; line = br.readLine())
        {
        	sb.append(line);
        	sb.append("\n");
        }
        this.init(sb.toString());
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getResponse(Type type) {
		Element bodyElement = (Element)this.envelope.getElementsByTagName("s:Body").item(0);
		NodeList bodyNodes = bodyElement.getChildNodes();
		if (bodyNodes.getLength()>0) {
			NodeList bodyResultNodes = bodyNodes.item(0).getChildNodes();
			if (bodyResultNodes.getLength()>0) {
				Element responseElem = (Element)bodyResultNodes.item(0);
				XMLSerializer serializer = new XMLSerializer();
				try {
					return (T)serializer.readObject(type, responseElem);
				}
				catch(Exception ex) {
					Log.e(ex);
				}
			}
		}
		return null;
	}
	
	public RuntimeException getFault(Class<?>[] exceptionTypes) {
		if (envelope!=null) {
			NodeList list = this.envelope.getElementsByTagName("s:Fault");
			if (list.getLength()>0) {
				Element faultElement = (Element)list.item(0);
				NodeList faultElemNodes = faultElement.getChildNodes();
				String faultcode = null;
				String faultstring = null;
				IException detailObj = null;
				for(int i=0; i<faultElemNodes.getLength(); i++) {
					Element faultElemNode = (Element)faultElemNodes.item(i);
					if (faultElemNode.getNodeName().equals("faultcode")) {
						faultcode = faultElemNode.getTextContent();
					}
					else if (faultElemNode.getNodeName().equals("faultstring")) {
						faultstring = faultElemNode.getTextContent();
					}
					else if (faultElemNode.getNodeName().equals("detail")) {
						NodeList details = faultElemNode.getChildNodes();
						if (details.getLength() == 1) {
							Element detail = (Element)details.item(0);
							XMLSerializer serializer = new XMLSerializer();
							try {
								detailObj = (IException)serializer.readObject(ExceptionDetail.class, detail);
							} catch (Exception e) {}
							
							if (exceptionTypes!=null) {
								for(Class<?> exType : exceptionTypes) {
									try {
										detailObj = (IException)serializer.readObject(exType, detail);
										break;
									}
									catch (Exception e) {}
								}
							}
						}
					}
				}
				int colonIdx = faultcode.lastIndexOf(':');
				if (colonIdx!=-1)
					faultcode = faultcode.substring(colonIdx+1); 
				FaultException exc = new FaultException(faultcode,faultstring);
				exc.setDetail(detailObj);
				return exc;
			}
		}
		return null;
	}
	
	private void init(String xmlString) {
		if (xmlString==null || xmlString.isEmpty())
			return;
		
		try {
			DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
			domFactory.setNamespaceAware(true);
			DocumentBuilder builder = domFactory.newDocumentBuilder();	
			InputSource is = new InputSource(new StringReader(xmlString));
			envelope = builder.parse(is);
		}
		catch(Exception ex) {
			Log.e(ex);
		}
	}	
	
	@Override
	public String toString() {
		try {
			String xmlString = StringUtil.toXmlString(envelope);
			return xmlString.substring(xmlString.indexOf("\n")+1);
		}
		catch(Exception ex) {
			Log.e(ex);
		}
		return null;
	}
	
	
}
